mod cli;

use ane_core::controller;
use ane_core::middleware::casbin_middleware;
use ane_core::security::LoginUser;
use ane_core::AppState;
use axum::{Extension, Router};
use clap::Parser;
use cli::{InitArgs, RunArgs};
use loginmanager::CurrentUser;
use sea_orm::{Database, DatabaseConnection};
use std::net::SocketAddr;
use tower_http::trace::TraceLayer;

#[tokio::main]
async fn main() {
    dotenv::dotenv().ok();
    if std::env::var_os("RUST_LOG").is_none() {
        std::env::set_var("RUST_LOG", "tower_http=info,middleware=info");
    }
    let args = crate::cli::WebCli::parse();
    // 默认运行run命令
    match args.command {
        Some(cli::Commands::Run(args)) => run(args).await,
        Some(cli::Commands::Init(args)) => init(args).await,
        None => run(cli::RunArgs::default()).await,
    }
}

async fn run(args: RunArgs) {
    ane_core::print_banner();
    let cnf = ane_core::load_config(&args.config_file);
    // 初始化日志系统
    let _guard = cnf.logger.init_log();

    let app_state = AppState::from_config(&cnf).await;

    let decoder = ane_core::security::AneBackend::new(app_state.clone(), cnf.secret.clone());
    let loginmanager = loginmanager::LoginManager::new(decoder)
        .login_view(cnf.loginmanager.login_view)
        .redirect(cnf.loginmanager.redirect);

    let app = Router::new()
        .nest("/file", uploader::get_route(None))
        .merge(controller::system::get_route("/api/system"))
        .merge(controller::get_route("/api"))
        .merge(controller::cms::manager::get_route("/api/cms/manager"))
        // ↑↑↑ 以上服务需要通过casbin请求判断
        .route_layer(axum::middleware::from_fn(casbin_middleware))
        .merge(controller::account::get_route("/api/account"))
        // ↑↑↑ 以上服务需要用户登陆才能访问
        // 认证登陆相关的api
        .route_layer(axum::middleware::from_extractor::<CurrentUser<LoginUser>>())
        .merge(controller::cms::view::get_route())
        .merge(controller::cms::api::get_route("/api/cms"))
        .merge(controller::sql_view::get_route())
        .merge(controller::auth::get_route("/auth"))
        .layer(loginmanager)
        .layer(TraceLayer::new_for_http())
        .layer(Extension(app_state.clone()))
        .layer(cnf.web.cors())
        .with_state(app_state)
        .merge(cnf.web.get_static_route())
        .nest_service("", cnf.web.html_server());

    let tcp_listener = tokio::net::TcpListener::bind(&cnf.web.addr).await.unwrap();
    tracing::info!("listening on http://{}", cnf.web.addr);
    axum::serve(
        tcp_listener,
        app.into_make_service_with_connect_info::<SocketAddr>(),
    )
    .await
    .unwrap();
}

async fn init(args: InitArgs) {
    let cnf = ane_core::load_config(&args.config_file);
    let conn: DatabaseConnection = Database::connect(cnf.database.get_connect_opt())
        .await
        .unwrap();
    if args.name == "db" {
        ane_core::init_db(&conn).await.unwrap();
    } else if args.name == "super_user" {
        ane_core::Mutation::init_super_user(&conn).await.unwrap();
    }
}
